package com.duke.shaking.widget;

import java.io.FileNotFoundException;
import java.io.IOException;

import com.androidquery.AQuery;
import com.androidquery.callback.AjaxCallback;
import com.androidquery.callback.AjaxStatus;
import com.androidquery.util.AQUtility;

import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class ImageCropView extends View {
	
	private Context context;
	//private int screenWidth;
	//private int screenHeight;
	private float currentRate = 1;
	private float oldRate = 1;
	private boolean isScaleFirst = true;
	private float originalLength;
	private Bitmap srcBitmap;
	private boolean isInitRate = true;
	private AQuery aQuery;
	
	private boolean isTranslateFirst = true;
	private float translateX = 0;
	private float translateY = 0;
	private float lastTranslateX = 0;
	private float lastTranslateY = 0;
	private float originalX;
	private float originalY;
	//private float cropWidth = 0;
	//private float cropHeight = 0;
	private Rect  cropRectSaved;
	private Paint paintMask;
	private int cropRectMargin = 30;
	
	public ImageCropView(Context context) {
		super(context);
		this.context = context;
		init(context);
	}

	public ImageCropView(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.context = context;
		init(context);
	}

	private void init(Context context) {
		aQuery = new AQuery(context);
		//setBackgroundColor(Color.BLACK);
	}

	public Bitmap getBitmap() {
		return srcBitmap;
	}
	
	private Rect getCropRect() {
		int measuredWidth= getMeasuredWidth(),
			measuredHeight = getMeasuredHeight(),
			cropWidth, cropHeight;
		
		if (measuredWidth <= 0 || measuredHeight <= 0) {
			return null;
		}
		
		if (measuredWidth > measuredHeight) {
			cropWidth = cropHeight = measuredHeight - cropRectMargin * 2;
			if (cropWidth <= 0) {
				cropWidth = cropHeight = measuredHeight;
			}
		}
		else {
			cropWidth = cropHeight = measuredWidth - cropRectMargin * 2;
			if (cropWidth <= 0) {
				cropWidth = cropHeight = measuredWidth;
			}
		}
		
		if (cropRectSaved != null
				&& cropRectSaved.width() == cropWidth
				&& cropRectSaved.height() == cropHeight) {
			return cropRectSaved;
		}
		
		cropRectSaved = new Rect();
		cropRectSaved.left = (measuredWidth - cropWidth) / 2;
		cropRectSaved.top = (measuredHeight - cropHeight) / 2;
		cropRectSaved.right = cropRectSaved.left + (int)cropWidth - 1;
		cropRectSaved.bottom = cropRectSaved.top + (int)cropHeight - 1;
		
		return cropRectSaved;
	}
	
	private void finishTranslate() {
		lastTranslateX = translateX;
		lastTranslateY = translateY;
		isTranslateFirst = true;
	}
	
	private void finishScale() {
		oldRate = currentRate;
		isScaleFirst = true;
	}

	private void computeScale(float currentLength) {
		Rect cropRect = getCropRect();
		if (cropRect == null 
			|| srcBitmap == null 
			|| srcBitmap.getWidth() <= 0 
			|| srcBitmap.getHeight() <= 0) {
			return;
		}
		
		float scale = (float) (oldRate * (currentLength / originalLength));
		float scaledWidth = scale * srcBitmap.getWidth(),
				scaledHeight = scale * srcBitmap.getHeight();
		if (scaledWidth < cropRect.width() ||
			scaledHeight < cropRect.height()) {
			return;
		}
		
		currentRate = scale;
		
		int left = (int)(getMeasuredWidth() / 2 - scaledWidth / 2);
		int top = (int)(getMeasuredHeight() / 2 - scaledHeight / 2);
		if (left + translateX > cropRect.left) {
			translateX = cropRect.left - left;
		}
		if (left + scaledWidth + translateX < cropRect.right) {
			translateX = cropRect.right - left - scaledWidth;
		}
		if (top + translateY > cropRect.top) {
			translateY = cropRect.top - top;
		}
		if (top + scaledHeight + translateY < cropRect.bottom) {
			translateY = cropRect.bottom - top - scaledHeight;
		}
		invalidate();
		
	}
	
	private void computeTranslate(float x, float y) {
		Rect cropRect = getCropRect();
		if (cropRect == null
			|| srcBitmap == null 
			|| srcBitmap.getWidth() <= 0 
			|| srcBitmap.getHeight() <= 0) {
			return;
		}
		float scaledWidth = currentRate * srcBitmap.getWidth(),
				scaledHeight = currentRate * srcBitmap.getHeight();
		int left = (int)(getMeasuredWidth() / 2 - scaledWidth / 2);
		int top = (int)(getMeasuredHeight() / 2 - scaledHeight / 2);
		if (left + x - originalX + lastTranslateX <= cropRect.left
			&& left + scaledWidth + x - originalX + lastTranslateX >= cropRect.right) {
			translateX = x - originalX + lastTranslateX;
		}
		
		if (top + y - originalY + lastTranslateY <= cropRect.top
			&& top + scaledHeight + y - originalY + lastTranslateY >= cropRect.bottom) {
			translateY = y - originalY + lastTranslateY;
		}
		invalidate();
			
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_MOVE:
			if (event.getPointerCount() == 2) {
				if (isScaleFirst) {
					originalLength = (float) Math.sqrt(Math.pow(event.getX(0)
							- event.getX(1), 2)
							+ Math.pow(event.getY(0) - event.getY(1), 2));
					isScaleFirst = false;
					if (!isTranslateFirst) {
						finishTranslate();
					}
				} else {
					float currentLength = (float) Math.sqrt(Math.pow(event.getX(0)
							- event.getX(1), 2)
							+ Math.pow(event.getY(0) - event.getY(1), 2));
					computeScale(currentLength);
				}
			}
			else if (event.getPointerCount() == 1) {
				if (isTranslateFirst) {
					originalX = event.getX();
					originalY = event.getY();
					isTranslateFirst = false;
					if (!isScaleFirst) {
						finishScale();
					}
				}
				else {
					computeTranslate(event.getX(), event.getY());
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			if (!isScaleFirst) {
				finishScale();
			}
			if (!isTranslateFirst) {
				finishTranslate();
			}
			
			break;
		}
		return true;
	}

	private void calcInitScaleRate(Bitmap bmp) {
		if (getMeasuredWidth() <= 0
			|| getMeasuredHeight() <= 0) {
			return;
		}
		float scale1 = (float)getMeasuredWidth() / (float)bmp.getWidth(),
				scale2 = (float)getMeasuredHeight() / (float)bmp.getHeight();
		if (scale1 > scale2) {
			currentRate = scale1;
		}
		else {
			currentRate = scale2;
		}
		oldRate = currentRate;
		isInitRate = false;
	}
	
	private boolean setBitmap(Bitmap bmp) {
		Rect rect = getCropRect();
		if (bmp == null 
			|| bmp.getWidth() <= 0
			|| bmp.getHeight() <= 0) {
			return false;
		}

		srcBitmap = bmp;
		isInitRate = true;
		if (rect == null) {
			return true;
		}
		
		calcInitScaleRate(bmp);
		invalidate();
		return true;
	}
	
	public boolean setBitmap(int resId) {
		Bitmap bmp;
		bmp = BitmapFactory.decodeResource(
				context.getResources(),
				resId);
		return setBitmap(bmp);
	}
	
	public boolean setBitmap(Uri uri) {
		Bitmap bmp = null;
		ContentResolver resolver = context.getContentResolver();
		//photo = BitmapFactory.decodeFile(uri.getPath());
		try {
			bmp = MediaStore.Images.Media.getBitmap(resolver, uri);
			return setBitmap(bmp);
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
		return false;
	}
	
	public boolean setBitmap(String url) {
		Bitmap bmp = aQuery.getCachedImage(url);
		if (bmp != null) {
			return setBitmap(bmp);
		}
		else {
			AQUtility.getCacheDir(context);
			aQuery.ajax(url, Bitmap.class, new AjaxCallback<Bitmap>() {
		        @Override
		        public void callback(String url, Bitmap object, AjaxStatus status) {
		        	if (object != null) {
		        		setBitmap(object);
		        	}
		        }});
		}
		return true;
	}
	
	public Rect getSelectionRect() {
		Rect cropRect = getCropRect();
		if (cropRect == null
			|| srcBitmap == null 
			|| srcBitmap.getWidth() <= 0
			|| srcBitmap.getHeight() <= 0) {
			return null;
		}
		
		float scale = currentRate;
		float scaledWidth = scale * srcBitmap.getWidth(),
				scaledHeight = scale * srcBitmap.getHeight();
		if (scaledWidth < cropRect.width()) {
			scaledWidth = cropRect.width();
		}
		if (scaledHeight < cropRect.height()) {
			scaledHeight = cropRect.height();
		}
		
		int measuredWidth= getMeasuredWidth(),
			measuredHeight = getMeasuredHeight();
		float left = measuredWidth / 2 - scaledWidth / 2;
		float top = measuredHeight / 2 - scaledHeight / 2;
		left += translateX;
		top += translateY;
		if (left > cropRect.left) {
			left = cropRect.left - translateX;
		}
		if (left + scaledWidth < cropRect.right) {
			left = cropRect.right - scaledWidth - translateX;
		}
		if (top > cropRect.top) {
			top = cropRect.top - translateY;
		}
		if (top + scaledHeight < cropRect.bottom) {
			top = cropRect.bottom - translateY - scaledHeight;
		}
		left = (cropRect.left - left) / scale;
		top = (cropRect.top - top) / scale;
		float width = cropRect.width() / scale;
		float height = cropRect.height() / scale;
		
		return new Rect((int)left, (int)top, (int)(left + width), (int)(top + height));
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		Rect cropRect = getCropRect();
		if (srcBitmap == null || cropRect == null) {
			super.onDraw(canvas);
			return;
		}

		if (isInitRate) {
			calcInitScaleRate(srcBitmap);
		}

		int measuredWidth= getMeasuredWidth(),
			measuredHeight = getMeasuredHeight();
		
		canvas.save();
		
		canvas.translate(translateX, translateY);
		canvas.scale(currentRate, currentRate, measuredWidth / 2,
				measuredHeight / 2);
		
		int left = measuredWidth / 2 - srcBitmap.getWidth() / 2;
		int top = measuredHeight / 2 - srcBitmap.getHeight() / 2;
		
		canvas.drawBitmap(srcBitmap, left, top, null);
		canvas.restore();
		
		if (paintMask == null) {
			paintMask = new Paint();
		}
		
		paintMask.setColor(Color.BLACK);
		paintMask.setAlpha(150);
		paintMask.setStyle(Style.FILL);
		canvas.drawRect(0, 0, cropRect.left, measuredHeight, paintMask);
		canvas.drawRect(cropRect.left, 0, cropRect.right, cropRect.top, paintMask);
		canvas.drawRect(cropRect.right, 0, measuredWidth, measuredHeight, paintMask);
		canvas.drawRect(cropRect.left, cropRect.bottom, cropRect.right, measuredHeight, paintMask);
		paintMask.setColor(Color.WHITE);
		paintMask.setAlpha(200);
		paintMask.setStyle(Style.STROKE);
		canvas.drawRect(cropRect, paintMask);
	}}
